package parse.xml;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class XmlDomParser {

	private Map<String,NodeCallMethod> nmMap=new HashMap<String,NodeCallMethod>();

	
	public void binding(String key,NodeCallMethod method){
		if(key!=null&&method!=null)nmMap.put(key, method);
	}
	
	
	public void parse(InputStream is){
		try {
			Document doc=createDocument(is);
			doProcessAll(doc);
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void doProcessAll(Document doc){
		Node lastN=doc.getLastChild();
		Path path=new Path();
		doProcess(lastN,path);
	}
	
	
	private void doProcess(Node node,Path path){
		if(node instanceof Element){
			String name=node.getNodeName();
			path.add(name);
			callMethod(path.toString(),node);
			
			// process the attribute
			NamedNodeMap attributes = node.getAttributes();
			for(int i=0;i<attributes.getLength();i++){
				Node att = attributes.item(i);
				path.add("@"+att.getNodeName());
				callMethod(path.toString(),att);
				path.remove();
			}
			
			
			// process the childs,childs is ensured non-null?
			NodeList childs=node.getChildNodes();
			for(int i=0;i<childs.getLength();i++){
				doProcess(childs.item(i),path);
			}
			path.remove();
		}else {
			// ignore
		}
	}
	private void callMethod(String key,Node node){
		NodeCallMethod method=nmMap.get(key);
		if(method!=null){
			method.process(node);
		}
	}
	private static class Path {
		
	    private List<String> nodeList = new ArrayList<String>();

	    public void add(String node) {
	      nodeList.add(node);
	    }

	    public void remove() {
	      if(nodeList.size()>0)
	         nodeList.remove(nodeList.size() - 1);
	    }

	    public String toString() {
	      StringBuffer buffer = new StringBuffer("/");
	      for (int i = 0; i < nodeList.size(); i++) {
	        buffer.append(nodeList.get(i));
	        if (i < nodeList.size() - 1) {
	          buffer.append("/");
	        }
	      }
	      return buffer.toString();
	    }
	  }
	private Document createDocument(InputStream is) throws ParserConfigurationException, SAXException, IOException{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

	    factory.setNamespaceAware(false);
	    factory.setIgnoringComments(true);
	    factory.setIgnoringElementContentWhitespace(false);
	    factory.setCoalescing(false);
	    factory.setExpandEntityReferences(true);

	    DocumentBuilder builder = factory.newDocumentBuilder();
	    return builder.parse(new InputSource(is));
	}
	
}
